from fastapi import FastAPI, UploadFile, File, HTTPException, Form, BackgroundTasks, Header
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, JSONResponse
from question_generator import QuestionGenerator
import os
import uuid
from typing import Optional
from pydantic import BaseModel
import shutil
from pathlib import Path
import logging
import subprocess
import sys
import requests  # 添加requests导入
import secrets
from datetime import datetime  # 添加datetime导入
import re
from urllib.parse import quote, unquote

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI()

# 配置CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 初始化题目生成器
generator = QuestionGenerator()

# 存储有效的API密钥
VALID_API_KEY = "ak_s41kObF3rTp5TEAvtT"

# 存储生成的token
token_store = {}

class CreateTokenRequest(BaseModel):
    uid: Optional[str] = None
    limit: Optional[int] = None

@app.post("/createApiToken")
async def create_api_token(request: CreateTokenRequest, api_key: str = Header(None, alias="Api-Key")):
    # 验证API密钥
    if not api_key or api_key != VALID_API_KEY:
        return JSONResponse(
            status_code=401,
            content={
                "code": 401,
                "message": "无效的API密钥"
            }
        )
    
    # 生成新token
    token = f"sk_{secrets.token_hex(16)}"
    expire_time = 7200  # 2小时
    
    # 存储token信息
    token_store[token] = {
        "uid": request.uid,
        "limit": request.limit,
        "expire_time": expire_time
    }
    
    logger.info(f"创建新token: {token}, UID: {request.uid}, 限制: {request.limit}")
    
    return {
        "code": 0,
        "message": "操作成功",
        "data": {
            "token": token,
            "expireTime": expire_time
        }
    }

# 确保上传目录存在
UPLOAD_DIR = Path("uploads").absolute()
UPLOAD_DIR.mkdir(exist_ok=True)

# 视频输出目录
OUTPUT_DIR = Path("output").absolute()
OUTPUT_DIR.mkdir(exist_ok=True, parents=True)

# 测试路由
@app.get("/")
async def root():
    logger.info("访问根路径")
    return {"message": "API is running"}

@app.get("/api/test")
async def test():
    logger.info("访问测试路径")
    return {"message": "Test endpoint is working"}

@app.get("/api/docmee/token")
async def get_docmee_token():
    try:
        # 使用真实API密钥
        api_key = "ak_s41kObF3rTp5TEAvtT"
        
        headers = {"Authorization": f"Bearer {api_key}"}
        
        # 创建token请求参数
        payload = {
            "uid": "user_123",  # 用户唯一ID
            "limit": 100,       # 使用次数限制
            "expire": 3600      # 有效期(秒)
        }
        
        response = requests.post(
            "https://open.docmee.cn/api/token/create",
            headers=headers,
            json=payload,
            timeout=10
        )
        
        if response.status_code == 200:
            token_data = response.json()
            return {
                "code": 0,
                "message": "成功",
                "data": {
                    "token": token_data["token"]
                }
            }
        else:
            logger.error(f"Docmee token生成失败: {response.status_code} - {response.text}")
            return {
                "code": 500,
                "message": f"Docmee API错误: {response.text}",
                "data": None
            }
            
    except Exception as e:
        logger.error(f"获取Docmee token异常: {str(e)}")
        return {
            "code": 500,
            "message": f"服务器错误: {str(e)}",
            "data": None
        }

class ChatRequest(BaseModel):
    message: str

@app.post("/api/chat")
async def chat(request: ChatRequest):
    try:
        logger.info(f"收到聊天请求: {request.message}")
        response = generator.chat(request.message)
        return {"response": response}
    except Exception as e:
        logger.error(f"聊天请求处理失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

class GenerateVideoRequest(BaseModel):
    file_path: str
    
    # 视频参数
    style: str = "default"
    speed: float = 1.0
    background_music: bool = False
    quality: str = "high"
    resolution: str = "1080p"

    # Edge-TTS 相关（第一优先级，默认启用）
    use_edge_tts: bool = True
    edge_voice: str = "zh-CN-XiaoxiaoNeural"
    
    # Azure TTS 相关
    azure_key: Optional[str] = None
    azure_region: str = "eastus"
    azure_voice: Optional[str] = None
    
    # 其他TTS选项
    local_voice_id: Optional[str] = None

# 添加静态文件路由
@app.get("/output/{filename}")
async def get_video(filename: str):
    video_path = OUTPUT_DIR / filename
    if not video_path.exists():
        raise HTTPException(status_code=404, detail="Video not found")
    return FileResponse(str(video_path))

# 检查视频生成状态的接口
@app.get("/api/video/status/{video_id}")
async def check_video_status(video_id: str):
    try:
        logger.info(f"检查视频状态请求: {video_id}")
        
        if not video_id:
            raise HTTPException(status_code=400, detail="视频ID不能为空")
        
        # 检查视频文件是否存在
        video_files = list(OUTPUT_DIR.glob("*.mp4"))
        video_file = None
        
        # 查找包含video_id的视频文件（支持新格式：name_timestamp.mp4）
        for file in video_files:
            filename = file.name
            if (filename == f"{video_id}.mp4" or 
                filename.endswith(f"_{video_id}.mp4")):
                video_file = file
                break
        
        if video_file and video_file.exists():
            # 检查文件大小（避免空文件或正在写入的文件）
            file_size_kb = video_file.stat().st_size / 1024
            
            if file_size_kb > 100:  # 文件大于100KB认为生成完成
                return {
                    "code": 0,
                    "data": {
                        "status": "completed",
                        "videoPath": f"/output/{video_file.name}",
                        "fileSize": file_size_kb
                    }
                }
            else:
                return {
                    "code": 0,
                    "data": {
                        "status": "processing",
                        "fileSize": file_size_kb
                    }
                }
        else:
            return {
                "code": 0,
                "data": {
                    "status": "processing"
                }
            }
            
    except Exception as e:
        logger.error(f"检查视频状态失败: {str(e)}")
        return {
            "code": 1,
            "message": f"检查视频状态失败: {str(e)}"
        }

# 添加上传文件静态路由
@app.get("/uploads/{filename}")
async def get_upload_file(filename: str):
    file_path = UPLOAD_DIR / filename
    if not file_path.exists():
        raise HTTPException(status_code=404, detail="File not found")
    return FileResponse(str(file_path))

# 文件上传接口
@app.post("/api/upload")
async def upload_file(file: UploadFile = File(...)):
    try:
        logger.info(f"收到文件上传请求: {file.filename}")
        
        # 检查文件类型
        allowed_types = ['.txt']
        file_extension = os.path.splitext(file.filename)[1].lower()
        if file_extension not in allowed_types:
            raise HTTPException(status_code=400, detail=f"不支持的文件类型: {file_extension}，仅支持.txt文件")
        
        # 检查文件大小 (10MB)
        content = await file.read()
        if len(content) > 10 * 1024 * 1024:  # 10MB
            raise HTTPException(status_code=400, detail="文件大小不能超过10MB")
        
        # 生成唯一文件名
        unique_filename = f"{uuid.uuid4().hex}{file_extension}"
        file_path = UPLOAD_DIR / unique_filename
        
        # 保存文件
        with open(file_path, "wb") as buffer:
            buffer.write(content)
        logger.info(f"文件保存成功: {file_path}")
        
        return {"file_path": str(file_path)}
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"文件上传失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

# 文件上传并直接生成视频
@app.post("/api/upload-and-generate")
async def upload_and_generate(background_tasks: BackgroundTasks, file: UploadFile = File(...)):
    try:
        logger.info(f"收到文件上传并生成视频请求: {file.filename}")
        
        # 检查文件类型
        allowed_types = ['.ppt', '.pptx', '.pdf', '.txt']
        file_extension = os.path.splitext(file.filename)[1].lower()
        if file_extension not in allowed_types:
            raise HTTPException(status_code=400, detail=f"不支持的文件类型: {file_extension}")
        
        # 检查文件大小 (50MB)
        content = await file.read()
        if len(content) > 50 * 1024 * 1024:  # 50MB
            raise HTTPException(status_code=400, detail="文件大小不能超过50MB")
        
        # 生成唯一文件名
        unique_filename = f"{uuid.uuid4().hex}{file_extension}"
        file_path = UPLOAD_DIR / unique_filename
        
        # 保存文件
        with open(file_path, "wb") as buffer:
            buffer.write(content)
        logger.info(f"文件保存成功: {file_path}")
        
        # 生成唯一输出文件名
        output_filename = f"video_{uuid.uuid4().hex}.mp4"
        output_path = OUTPUT_DIR / output_filename
        
        # 构建命令（默认使用Edge-TTS）
        cmd = [
            sys.executable, 
            "scripts/ppt_to_video.py",
            str(file_path),
            "--output", str(output_path),
            "--edge_voice", "zh-CN-XiaoxiaoNeural"
        ]
        
        logger.info(f"执行视频生成命令: {' '.join(cmd)}")
        
        # 在后台运行视频生成任务
        background_tasks.add_task(run_video_generation, cmd, output_path)
        
        # 保存视频信息到数据库（伪代码）
        # video_info = {
        #     "id": str(uuid.uuid4()),
        #     "name": file.filename,
        #     "video_path": str(output_path),
        #     "created_at": datetime.now().isoformat()
        # }
        # save_video_to_db(video_info)
        
        return {
            "success": True,
            "message": "视频生成任务已开始",
            "video_url": f"/output/{output_filename}",
            "video_id": str(uuid.uuid4())  # 返回视频ID用于后续查询
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"文件上传并生成视频失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

# 获取视频列表
@app.get("/api/videos")
async def get_video_list():
    try:
        # 伪代码：从数据库获取视频列表
        # videos = get_videos_from_db()
        videos = [
            {"id": "1", "name": "演示视频1", "created_at": "2023-01-01"},
            {"id": "2", "name": "演示视频2", "created_at": "2023-01-02"}
        ]
        return {"success": True, "data": videos}
    except Exception as e:
        logger.error(f"获取视频列表失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))


class GenerateQuestionsRequest(BaseModel):
    core_words: Optional[str] = None
    question_type: Optional[str] = None
    count: int = 5
    difficulty: str = "中等"
    file_path: Optional[str] = None

@app.post("/api/generate-questions")
async def generate_questions(request: GenerateQuestionsRequest):
    try:
        logger.info(f"生成题目请求: {request}")
        
        # 如果有文件路径，则处理文档
        # 统一调用生成题目方法
        response = generator.generate_questions(
            core_words=request.core_words,
            question_type=request.question_type,
            count=request.count,
            difficulty=request.difficulty,
            file_path=request.file_path
        )
        return {"response": response}
    except Exception as e:
        logger.error(f"生成题目失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/api/clear-history")
async def clear_history():
    try:
        logger.info("清空历史记录")
        generator.clear_history()
        return {"message": "历史记录已清空"}
    except Exception as e:
        logger.error(f"清空历史记录失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

class ExportRequest(BaseModel):
    content: str
    format: str  # 支持的格式：txt, pdf, docx, html, markdown
    source_type: str  # 'keywords' 或 'file'
    source_name: str  # 核心词或文件名

@app.post("/api/export-questions")
async def export_questions(request: ExportRequest):
    try:
        logger.info(f"题目导出请求开始: {request.source_type}, 格式: {request.format}")
        logger.info(f"源名称: {request.source_name}")
        logger.info(f"内容长度: {len(request.content)} 字符")
        
        # 生成时间戳
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        logger.info(f"生成时间戳: {timestamp}")
        
        # 安全处理文件名，保留Unicode字符但移除特殊符号
        def safe_filename(name: str) -> str:
            try:
                # 允许Unicode字符（包括中文）、字母、数字、下划线、点、减号
                # 移除其他特殊符号
                safe_name = re.sub(r'[^\w\u4e00-\u9fff\-\.]+', '_', name)
                # 限制长度
                if len(safe_name) > 100:  # 增加长度限制以适应中文
                    safe_name = safe_name[:100]
                return safe_name
            except Exception as e:
                logger.error(f"文件名处理失败: {e}")
                return f"file_{timestamp}"
        
        # 根据生成方式确定文件名
        try:
            if request.source_type == 'keywords':
                safe_source_name = safe_filename(request.source_name)
                base_filename = f"{safe_source_name}_{timestamp}"
            elif request.source_type == 'file':
                # 去掉文件扩展名
                file_basename = os.path.splitext(request.source_name)[0]
                safe_source_name = safe_filename(file_basename)
                base_filename = f"{safe_source_name}_{timestamp}"
            else:
                base_filename = f"题目_{timestamp}"
            
            logger.info(f"生成的基础文件名: {base_filename}")
        except Exception as e:
            logger.error(f"文件名生成失败: {e}")
            base_filename = f"questions_{timestamp}"
        
        # 确保输出目录存在
        try:
            export_dir = OUTPUT_DIR / "exports"
            export_dir.mkdir(exist_ok=True)
            logger.info(f"导出目录: {export_dir}")
        except Exception as e:
            logger.error(f"创建导出目录失败: {e}")
            raise HTTPException(status_code=500, detail=f"无法创建导出目录: {str(e)}")
        
        # 创建临时markdown文件
        temp_md_file = export_dir / f"{base_filename}.md"
        logger.info(f"临时文件路径: {temp_md_file}")
        
        # 将内容写入markdown文件
        try:
            with open(temp_md_file, 'w', encoding='utf-8') as f:
                f.write(request.content)
            logger.info("内容写入临时文件成功")
        except Exception as e:
            logger.error(f"写入临时文件失败: {e}")
            raise HTTPException(status_code=500, detail=f"无法创建临时文件: {str(e)}")
        
        # 根据格式导出
        try:
            if request.format == 'txt':
                output_file = export_dir / f"{base_filename}.txt"
                logger.info(f"生成TXT文件: {output_file}")
                # 直接保存为txt
                with open(output_file, 'w', encoding='utf-8') as f:
                    f.write(request.content)
            elif request.format == 'markdown':
                output_file = export_dir / f"{base_filename}.md"
                logger.info(f"生成Markdown文件: {output_file}")
                # markdown文件已经创建，直接使用
                # 不需要额外处理，temp_md_file就是最终文件
                temp_md_file = None  # 避免删除
            elif request.format == 'pdf':
                output_file = export_dir / f"{base_filename}.pdf"
                logger.info(f"生成PDF文件: {output_file}")
                # 获取模板路径
                template_path = Path(__file__).parent / "templates" / "questions.latex"
                
                # 使用pandoc导出为PDF，支持中文
                cmd = [
                    "pandoc", str(temp_md_file), 
                    "-o", str(output_file),
                    "--pdf-engine=xelatex",
                    "--template", str(template_path)
                ]
                
                # 如果模板不存在，使用基本配置
                if not template_path.exists():
                    cmd = [
                        "pandoc", str(temp_md_file), 
                        "-o", str(output_file),
                        "--pdf-engine=xelatex",
                        "--variable", "geometry:margin=2.5cm",
                        "--variable", "fontsize=12pt",
                        "--variable", "linestretch=1.2",
                        "--toc"
                    ]
                    # 尝试设置中文字体，如果失败则忽略
                    try:
                        cmd.extend(["--variable", "CJKmainfont=SimSun"])
                    except:
                        pass
                
                logger.info(f"执行pandoc命令: {' '.join(cmd)}")
                subprocess.run(cmd, check=True)
                output_file.rename(output_file)  # 改回中文名
            elif request.format == 'docx':
                output_file = export_dir / f"{base_filename}.docx"
                logger.info(f"生成DOCX文件: {output_file}")
                # 使用pandoc导出为DOCX
                cmd = ["pandoc", str(temp_md_file), "-o", str(output_file)]
                logger.info(f"执行pandoc命令: {' '.join(cmd)}")
                subprocess.run(cmd, check=True)
            elif request.format == 'html':
                output_file = export_dir / f"{base_filename}.html"
                logger.info(f"生成HTML文件: {output_file}")
                # 使用pandoc导出为HTML
                cmd = ["pandoc", str(temp_md_file), "-o", str(output_file), "--standalone"]
                logger.info(f"执行pandoc命令: {' '.join(cmd)}")
                subprocess.run(cmd, check=True)
            else:
                logger.error(f"不支持的导出格式: {request.format}")
                raise HTTPException(status_code=400, detail="不支持的导出格式")
            
            logger.info(f"格式转换完成: {output_file}")
        except subprocess.CalledProcessError as e:
            logger.error(f"Pandoc命令执行失败: {e}")
            raise HTTPException(status_code=500, detail=f"文件转换失败: {str(e)}")
        except Exception as e:
            logger.error(f"文件格式转换异常: {e}")
            raise HTTPException(status_code=500, detail=f"文件转换异常: {str(e)}")
        
        # 清理临时文件（除非是markdown格式）
        try:
            if temp_md_file and temp_md_file.exists():
                temp_md_file.unlink()
                logger.info("临时文件清理成功")
        except Exception as e:
            logger.warning(f"清理临时文件失败: {e}")
        
        logger.info(f"导出成功: {output_file}")
        
        return {
            "success": True,
            "message": "导出成功",
            "filename": output_file.name,
            "download_url": f"/exports/{output_file.name}"
        }
        
    except HTTPException:
        raise  # 重新抛出HTTP异常
    except subprocess.CalledProcessError as e:
        logger.error(f"Pandoc导出失败: {e}")
        return {
            "success": False,
            "message": f"导出失败: pandoc处理错误 - {str(e)}"
        }
    except Exception as e:
        logger.error(f"导出题目失败: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"导出失败: {str(e)}")

# 添加导出文件下载路由
@app.get("/exports/{filename:path}")
async def get_export_file(filename: str):
    # 解码URL编码的文件名
    decoded_filename = unquote(filename)
    
    export_dir = OUTPUT_DIR / "exports"
    file_path = export_dir / decoded_filename
    if not file_path.exists():
        raise HTTPException(status_code=404, detail="File not found")
    
    # 获取文件扩展名
    file_extension = file_path.suffix.lower()
    
    # 处理中文文件名的函数
    def create_content_disposition(filename: str) -> str:
        try:
            # 尝试编码为ASCII，如果成功则直接使用
            filename.encode('ascii')
            return f"attachment; filename={filename}"
        except UnicodeEncodeError:
            # 包含非ASCII字符，使用RFC 6266方式
            # 创建一个安全的fallback文件名（仅 ASCII 字母、数字、下划线、点、减号）
            safe_filename = re.sub(r'[^A-Za-z0-9_\-\.]+', '_', filename)
            # 创建UTF-8编码的文件名
            encoded_filename = quote(filename)
            return f"attachment; filename={safe_filename}; filename*=UTF-8''{encoded_filename}"
    
    # 设置媒体类型和是否强制下载
    if file_extension in ['.txt', '.md']:
        # 对于txt和markdown文件，强制下载
        media_type = 'text/plain' if file_extension == '.txt' else 'text/markdown'
        return FileResponse(
            str(file_path), 
            media_type=media_type,
            headers={"Content-Disposition": create_content_disposition(decoded_filename)}
        )
    elif file_extension == '.pdf':
        return FileResponse(
            str(file_path), 
            media_type='application/pdf',
            headers={"Content-Disposition": create_content_disposition(decoded_filename)}
        )
    elif file_extension == '.docx':
        return FileResponse(
            str(file_path), 
            media_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            headers={"Content-Disposition": create_content_disposition(decoded_filename)}
        )
    elif file_extension == '.html':
        return FileResponse(
            str(file_path), 
            media_type='text/html',
            headers={"Content-Disposition": create_content_disposition(decoded_filename)}
        )
    else:
        # 默认情况，强制下载
        return FileResponse(
            str(file_path),
            headers={"Content-Disposition": create_content_disposition(decoded_filename)}
        )

if __name__ == "__main__":
    import uvicorn
    logger.info("启动服务器...")
    uvicorn.run(
        app, 
        host="127.0.0.1", 
        port=8000, 
        log_level="info",
        timeout_keep_alive=600  # 增加超时时间到10分钟
    )
